package it.softphone.cmc.server.rest;

import it.softphone.cmc.server.service.EntityManagerBaseService.EMTask;
import it.softphone.cmc.server.service.QueryTask;
import it.softphone.cmc.server.service.TenantService;
import it.softphone.cmc.server.service.exception.CMCException;
import it.softphone.cmc.shared.entity.Account;
import it.softphone.cmc.shared.entity.Account.Preference;
import it.softphone.cmc.shared.entity.BaseResultSet;
import it.softphone.cmc.shared.entity.Tenant;
import it.softphone.cmc.shared.security.Permissions;
import it.softphone.cmc.shared.security.Roles;
import it.softphone.commons.json.server.JSONException;

import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;

@Path("/account")
public class CMCAccountManager extends RESTServiceAware  {

	/**
	 * 
	 * @author bsorrentino
	 *
	 */
	public static class  AccountResultSet extends BaseResultSet<Account> implements Account.ResultSet {
		
	}
	
	/**
	 * 
	 * @author bsorrentino
	 *
	 */
	public static class  AccountPreferenceResultSet  extends BaseResultSet<Account.Preference> implements Account.Preference.ResultSet {
	}
	
	/**
	 * Get All Accounts
	 *
	 * @security Require Administration role
	 * @since 3.1.0
	 * @return json result set { "result": [ {}, .. ]  }
	 */
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	@Path("all")
	@RequiresRoles( Roles.ADMINISTRATOR )
	public it.softphone.commons.json.server.JSONObject getAccounts( /*@QueryParam("tenant") String tenant*/ ) throws CMCException {
		
		AccountResultSet result = tenant().execAsSystemTenant( new TenantService.Task<AccountResultSet>() {

				@Override
			public AccountResultSet exec() throws CMCException {

					final AccountResultSet resultSet = new AccountResultSet();
					
					system().find( "SELECT r FROM AccountEntity r", null,  new QueryTask<List<Account>>() {

						@Override
						public void run(List<Account> result) throws Exception {
							
							
							resultSet.setResult(result);

						}
						
					});
					
					return resultSet;
					
				}
				
			});

		final it.softphone.commons.json.server.JSONObject json_result = new it.softphone.commons.json.server.JSONObject( result );
		
		return json_result;
		
				
	}
	
	/**
	 * Retrieve account preferences - Account preference is the tenant on which there is registered
	 * 
	 * @param accountId mail or phone number (mandatory)
	 * @return json result set { "result": [ {}, .. ]  }
	 * @throws CMCException
	 */
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	@Path("pref")
	public it.softphone.commons.json.server.JSONObject getAccountPreferences( @QueryParam("account") final String accountId ) throws CMCException {
		
			
			Account account = null;
			
			if( accountId != null  ) {
				
				checkRole( Roles.ADMINISTRATOR );
				account = system().findAccountById(accountId);
				if( null==account ) throw new CMCRestException( Status.NOT_FOUND, String.format("account [%s] not found!", accountId));
			}
			else {
				
				account = getloggedAccount();
				if( null==account ) throw new CMCRestException( Status.PRECONDITION_FAILED, "no account is logged in!");
			}
			
			final AccountPreferenceResultSet resultSet = new AccountPreferenceResultSet();
			
			system().findAccountPreferences(account, new QueryTask<List<Preference>>() {

				@Override
				public void run(List<Preference> result) throws Exception {

					resultSet.setResult(result);
					
				}
				
			});							
							
				
			return new it.softphone.commons.json.server.JSONObject(resultSet);		
			
	}
	
	/**
	 * Create a new Account preference 
	 * Account preference is the tenant on which there is registered
	 * 
	 * @param key *Tenant attribute used for search ( actually only 'name' is supported )*
	 * @param value  *Key value*
	 * @return json object created
	 */
	@POST
	@Produces(MediaType.APPLICATION_JSON)
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Path("pref")
	public it.softphone.commons.json.server.JSONObject createAccountPreference( @FormParam("key") String key, @FormParam("value")  String value ) throws CMCException {
		
		final Account loggedAccount = getloggedAccount();
		
		final Tenant tenant = system().findTenantById(key, value);
		
		if( tenant == null ) {
			
			throw new CMCRestException(Status.NOT_FOUND, String.format("tenant for key [%s] and value [%s] not found!", key, value));
		}
		
		final Account.Preference preference = system().makeAccountPreference();
		
		preference.setTenant(tenant);
		
		system().createAccountPreference(loggedAccount, preference);
		
		
		return new it.softphone.commons.json.server.JSONObject(preference);
		
	}

	/**
	 * 
	 * @param accountId
	 * @return
	 * @throws JSONException 
	 */
	@POST
	@Consumes( {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON})
	@Produces(MediaType.TEXT_PLAIN)
	@Path("save")
	@RequiresPermissions( { Permissions.Account.UPDATE, Permissions.Account.SELECT} )
	public Response updateAccountPOST( String paramAccount  ) throws CMCException, JSONException {
		return updateAccount( paramAccount );
	}

	/**
	 * 
	 * @param accountId
	 * @return
	 * @throws JSONException 
	 */
	@PUT
	@Consumes( {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON})
	@Produces(MediaType.TEXT_PLAIN)
	@RequiresPermissions( { Permissions.Account.UPDATE, Permissions.Account.SELECT} )
	public Response updateAccount( String paramAccount  ) throws CMCException, JSONException
	{
			it.softphone.commons.json.server.JSONObject p = new it.softphone.commons.json.server.JSONObject( paramAccount );
			
			final Account pAccount = system().makeAccountFromJson( p );
			
			
			return system().execNewTransaction( "updateAccount", new EMTask<Response>() {

				@Override
				public Response exec(EntityManager em) throws Exception {
					
					Account account = system().findAccountById(pAccount.getId());
					
					if( account==null ) {
						return Response.status(Status.NOT_FOUND).entity( "Account not found!").build();
					}
					
					if( pAccount.getDeviceId()!=null &&  !pAccount.getDeviceId().isEmpty() ) {
						account.setDeviceId(pAccount.getDeviceId());
					}
					
					it.softphone.commons.json.server.JSONObject attrMap = account.encodeAttributes();
					it.softphone.commons.json.server.JSONObject extraAttributes =  pAccount.encodeAttributes();
				
					@SuppressWarnings("unchecked")
					java.util.Iterator<String> ii = extraAttributes.keys();
					while( ii.hasNext() ) {
						final String key = ii.next();
						
						final String value = extraAttributes.getString(key);
						
						logger.debug( String.format("[%s]=[%s]", key, value ));
						attrMap.put( key, value);
					}
					
					account.getAttributes().setUpdated( new java.util.Date() );
					account.decodeAttributes(attrMap);
				
					system().storeAccount(account);
					
					return Response.ok().build();

				}
				
			});
		
					
	}
	
}
